package com.gzsxy.utils;

import com.sun.org.apache.regexp.internal.RE;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;

/**
 * @author xiaolong
 * @version 1.0
 * @description: 实体bean 转成 map对象
 * @date 2021/9/30 17:04
 */

public class ReflectUtil {

    private static final Logger log = LoggerFactory.getLogger(ReflectUtil.class);

    /**
     * @description: Bean方法的缓存
     * @param
     * @return
     */
    private final static Map<String, Map<String, Method>> METHOD_MAP = new ConcurrentHashMap<>();

    /**
     * @param type get 或者 set
     * @param name 属性名
     * @return
     * @description: 获取get与set方法名
     */
    public static String creatMethodName(String type, String name) {
        return type + name.substring(0, 1).toUpperCase() + name.substring(1);
    }

    /**
     * @param obj      bean对象
     * @param fileName 属性名
     * @return
     * @description: 得到get方法
     */
    public static Method getGETMethod(Object obj, String fileName) {
        if (obj == null) {
            return null;
        }
        //获取bean的class
        Class<? extends Object> clazz = obj.getClass();
        //获取类名下的所有 map属性名和方法值
        Map<String, Method> map = METHOD_MAP.get(clazz.getName());
        if (map == null) {
            map = new HashMap<>(10);
            METHOD_MAP.put(clazz.getName(), map);
        }
        //获取get与set方法名
        String methodName = creatMethodName("get", fileName);
        try {
            //获取方法
            Method method = map.get(methodName);
            if (method == null) {
                //获取方法
                method = clazz.getMethod(methodName);
                map.put(methodName, method);
            }
            return method;
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * @param obj      bean对象
     * @param fileName 属性名
     * @return 方法值
     * @description: 得到实体类对象（属性值存在对象中）
     */
    public static Object getObjectValue(Object obj, String fileName) {
        //获取属性名的方法
        Method method = getGETMethod(obj, fileName);
        return getObjectValue(obj, method);
    }

    /**
     * @param obj    bean对象
     * @param method 属性值方法
     * @return 方法值
     * @description: 得到实体类对象（属性值存在对象中）
     */
    public static Object getObjectValue(Object obj, Method method) {
        if (method == null || obj == null) {
            return null;
        }
        try {
            //返回的是方法值
            return method.invoke(obj);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * @param bean 对象
     * @return map
     * @description: bean 转成 map
     */
    public static Map<String, Object> convertBeanToMap(Object bean) {
        Map<String, Object> map = new HashMap<>(4);
        //获取class对象
        Class<?> clazz = bean.getClass();
        try {
            //获取全部属性
            Field[] declaredFields = clazz.getDeclaredFields();
            for (Field field : declaredFields) {
                //获取属性名
                String name = field.getName();
                //若为序列化字段
                if ("serialVersionUID".equals(name)) {
                    continue;
                }
                //得到字段值
                Object obj = getObjectValue(bean, name);
                if (obj != null) {
                    map.put(name, obj);
                }
            }
            //获取父类的class
            Class<?> superclazz = clazz.getSuperclass();
            //判断不为父类不为Object.class，说明没有父类了
            while (superclazz != Object.class) {
                //获取全部属性
                declaredFields = superclazz.getDeclaredFields();
                for (Field field : declaredFields) {
                    String name = field.getName();
                    if ("serialVersionUID".equals(name)) {
                        continue;
                    }
                    Object obj = getObjectValue(bean, name);
                    if (obj != null) {
                        map.put(name, obj);
                    }
                }
                superclazz = superclazz.getSuperclass();
            }
        } catch (SecurityException e) {
            e.printStackTrace();
            System.out.println("反射初始化异常");
        }
        return map;
    }


    /**
     * @description: map转Bean
     * @param descClazz
     * @param map
     * @param ignoreNull true 遇到String ==null ,替换成空格
     * @return
     */
    public static <T> T converMapToBean(Class<T> descClazz,Map<String,Object> map,boolean ignoreNull) throws InstantiationException, IllegalAccessException {
        T desc = descClazz.newInstance();
        convertMapToBean(desc,map,ignoreNull);
        return desc;
    }


    /**
     * @description: map转Bean
     * @param desc
     * @param src
     * @param ignoreNull true 遇到String ==null ,替换成空格
     * @return
     */
    public static void convertMapToBean(Object desc,Map<String,Object> src,boolean ignoreNull){
        if (desc == null || src == null){
            log.error("src or desc is null");
            return;
        }
        Object value = null;
        VirtualMethod method = null;
        Class<?> type = null;
        VirtualBean descVirtual = ReflectUtilCache.getVirtualBean(desc.getClass());
        Set<Map.Entry<String,VirtualMethod>> entrySet = descVirtual.entrySet();
        for (Map.Entry<String,VirtualMethod> entry :entrySet){
            value = src.get(entry.getKey());
            method = entry.getValue();
            type = method.getType();
            if (value == null && type == String.class && ignoreNull){
                method.write(desc,"");
            } else {
                method.write(desc,value);
            }
        }
    }


    /**
     * @description: 将javaBean转为另一个javaBean字段名称需要一致 源javaBean的字段需要包括目标javaBean的字段，字段不存在，忽略
     * @param src 源javaBean
     * @param clazz 目标javaBean类型
     * @return
     */
    public static <T> T convertBeanToBean(Object src,Class<T> clazz,boolean ignoreNull){
        T resultBean;
        try {
            //通过构造器创建对象
            resultBean = clazz.newInstance();
        } catch (Exception e) {
            log.error(e.getMessage(),e);
            throw new IllegalStateException(e);
        }
        editBeanfromBean(src,resultBean,ignoreNull);
        return resultBean;

    }

    /**
     * @description: 将javaBean转为另一个javaBean字段名称需要一致 源javaBean的字段需要包括目标javaBean的字段，字段不存在，忽略
     * @param src 源javaBean
     * @param desc 目标javaBean类型
     * @param ignoreNull true:忽略null  false:不忽略null
     * @return
     */
    public static void editBeanfromBean(Object src,Object desc,boolean ignoreNull){
        if (src == null || desc == null){
            log.error("src or desc is null");
            return;
        }
        VirtualBean srcVirtual = ReflectUtilCache.getVirtualBean(src.getClass());
        VirtualBean descVirtual = ReflectUtilCache.getVirtualBean(desc.getClass());
        VirtualMethod srcMethod = null;
        VirtualMethod descMethod = null;
        Set<Map.Entry<String, VirtualMethod>> entrySet = descVirtual.entrySet();
        for (Map.Entry<String, VirtualMethod> entry : entrySet) {
            srcMethod = srcVirtual.getMethod(entry.getKey());
            if (srcMethod != null){
                Object value = srcMethod.read(src);
                if (!ignoreNull || value != null){
                    descMethod = entry.getValue();
                    descMethod.write(desc,value);
                }
            }

        }

    }


    public static class VirtualMethod{
        private final Method read;

        private final Method write;

        private final Class<?> type;

        private final Field field;

        /**
         * @description: 封装该属性的get、set方法
         * @param
         * @return
         */
        public VirtualMethod(Field field,Class<?> instan){
            //属性名
            String fieldName = field.getName();
            //属性的类型
            this.type  = field.getType();
            //获取属性方法
            this.read = methodGET(instan,fieldName);
            //不为常类
            if (!Modifier.isFinal(field.getModifiers())){
                this.write = methodSET(instan,fieldName,this.type);
            } else {
                this.write = null;
            }
            this.field = field;
        }

        public Class<?> getType(){
            return type;
        }

        /**
         * @description: 写入数据
         * @param obj
         * @param value
         * @return
         */
        public void write(Object obj,Object value){
            try {
                if (this.write == null){
                    log.error("当前属性{}写入方法不存在",field.getName());
                    return;
                }
                this.write.invoke(obj,value);
            } catch (Exception e) {
                log.error(e.getMessage(),e);
                throw new IllegalStateException(e);
            }
        }

        /**
         * @description: 读取数据
         * @param obj
         * @return
         */
        public Object read(Object obj){
            try {
                if (this.read == null){
                    log.error("当前属性{}读取方法不存在",field.getName());
                    return null;
                }
                return this.read.invoke(obj);
            } catch (Exception e) {
                log.error(e.getMessage(),e);
                throw new IllegalStateException(e);
            }
        }

        /**
         * @description: 反射方法，通过get方法获取对象属性
         * @param
         * @return
         */
        private Method methodGET(Class<?> clazz,String fieldname){
            String methodName = "get"+fieldname.substring(0,1).toUpperCase()+fieldname.substring(1);
            try {
                return clazz.getMethod(methodName);
            } catch (Exception e) {
                log.error(e.getMessage());
            }
            return null;
        }


        /**
         * @description: 反射方法，通过set方法获取对象属性
         * @param
         * @return
         */
        public Method methodSET(Class<?> instan,String fieldName,Class<?> type){
            String methodName = "set"+ fieldName.substring(0,1).toUpperCase() +fieldName.substring(1);

            try {
                return instan.getMethod(methodName,new Class[] {type});
            } catch (Exception e) {
                log.error(e.getMessage());
            }
            return null;
        }


        public Field getField(){
            return field;
        }




    }


    //测试
    public static void main(String[] args) {
        OmsNodeInfo omsNodeInfo = new OmsNodeInfo();
        omsNodeInfo.setId(1000);
        Map<String, Object> map = convertBeanToMap(omsNodeInfo);
        System.out.println(map);
    }

}

/**
 * @description: 虚拟bean
 * @param
 * @return
 */
class VirtualBean{
        private final static Logger log = LoggerFactory.getLogger(VirtualBean.class);

        private final Map<String, ReflectUtil.VirtualMethod> method;

        private VirtualBean(){
            this.method = new HashMap<String, ReflectUtil.VirtualMethod>(6);
        }

        /**
         * @description: 判断是否存在
         * @param  fieldName
         * @return
         */
        public boolean hasMethod(String fieldName){
            return method.containsKey(fieldName);
        }

       /**
        * @description: 获取方法
        * @param  fieldName
        * @return
        */
        public ReflectUtil.VirtualMethod getMethod(String fieldName){
            return this.method.get(fieldName);
        }

        /**
         * @description: 获取缓存字段
         * @param
         * @return
         */
        public Set<String> getCacheField(){
            return this.method.keySet();
        }

        /**
         * @description: 获取全部字段
         * @param
         * @return
         */
        public Set<Map.Entry<String, ReflectUtil.VirtualMethod>> entrySet(){
            return this.method.entrySet();
        }

        /**
         * @description: 创建类下的所有属性的get、set方法
         * @param
         * @return
         */
        public static VirtualBean createVirtualBean(Class<?> clazz){
            VirtualBean virtualBean = new VirtualBean();
            //获取全部属性
            Field[] declaredFields = clazz.getDeclaredFields();
            for (Field field : declaredFields) {
                //getModifiers此方法的返回类型为int ，它返回一个整数值，该整数值表示类或接口的修饰符。
                if (Modifier.isStatic(field.getModifiers())){
                    //如果为静态属性
                    continue;
                }
                //<属性名，属性的get、set方法>
                virtualBean.method.put(field.getName(),new ReflectUtil.VirtualMethod(field,clazz));
            }
            //处理父类
            Class<?> supclazz = clazz.getSuperclass();
            while (supclazz != Object.class){
                //父类的全部属性
                declaredFields = supclazz.getDeclaredFields();
                for (Field field : declaredFields) {
                    if (Modifier.isStatic(field.getModifiers())){
                        continue;
                    }
                    try {
                        String name = field.getName();
                        if (!virtualBean.method.containsKey(name)){
                            virtualBean.method.put(name,new ReflectUtil.VirtualMethod(field,clazz));
                        }
                    } catch (Exception e) {
                        log.error(e.getMessage(),e);
                    }
                }
                supclazz = supclazz.getSuperclass();
            }
            return virtualBean;

        }



}




/**
 * @description: 缓存
 * @param
 * @return
 */
class ReflectUtilCache{
        private final static Map<String,VirtualBean> CACHE_VIRTUALBEAN = new HashMap<>();

        private final static  Object LOCK = new Object();

        public final static VirtualBean getVirtualBean(Class<?> clazz){
            //获取类名
            String srcName = clazz.getName();
            VirtualBean virtualBean = CACHE_VIRTUALBEAN.get(srcName);
            if (virtualBean == null){
                synchronized (LOCK){
                    virtualBean = CACHE_VIRTUALBEAN.get(srcName);
                    if (virtualBean == null){
                        //创建类下的所有属性的get、set方法
                        virtualBean = VirtualBean.createVirtualBean(clazz);
                        //<类名,类下的所有属性的get、set方法 >
                        CACHE_VIRTUALBEAN.put(srcName,virtualBean);
                    }
                }
            }
            return virtualBean;
        }
}



